package com.edu.internship.util;

import com.google.common.collect.Lists;
import org.apache.commons.lang3.ArrayUtils;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.BeanWrapper;
import org.springframework.beans.BeanWrapperImpl;

import java.beans.PropertyDescriptor;
import java.util.HashSet;
import java.util.List;
import java.util.Objects;
import java.util.Set;
import java.util.function.Supplier;

/**
 * code is far away from bug with the animal protecting
 *
 * @author lishiquan
 * @description
 * @date Create in 2018/6/21 10:39
 */
public class BeanExUtils extends BeanUtils {

    private BeanExUtils() {
    }

    private static String[] getNullPropertyNames(Object source, boolean flag) {
        final BeanWrapper src = new BeanWrapperImpl(source);
        PropertyDescriptor[] pds = src.getPropertyDescriptors();
        Set<String> emptyNames = new HashSet<>();
        for (PropertyDescriptor pd : pds) {
            Object srcValue = src.getPropertyValue(pd.getName());
            if (Objects.isNull(srcValue)) {
                emptyNames.add(pd.getName());
            }
            if (flag && srcValue instanceof String) {
                emptyNames.add(pd.getName());
            }
        }
        String[] result = new String[emptyNames.size()];
        return emptyNames.toArray(result);
    }

    /**
     * 复制的时候忽略null而不忽略String的""
     *
     * @param src    源对象
     * @param target 目标对象
     */
    public static void copyPropertiesIgnoreNull(Object src, Object target) {
        copyProperties(src, target, getNullPropertyNames(src, false));
    }

    /**
     * 复制的时候忽略null和String的"",空串
     * 参考apache的StringUtils.isBlank()
     *
     * @param src    源对象
     * @param target 目标对象
     */
    public static void copyPropertiesIgnoreBlank(Object src, Object target) {
        copyProperties(src, target, getNullPropertyNames(src, true));
    }

    /**
     * 复制的时候忽略null和String的"",空串
     * 参考apache的StringUtils.isBlank()
     *
     * @param src    源对象
     * @param target 目标对象
     */
    public static void copyPropertiesExcludeFields(Object src, Object target, String[] fieldNames) {
        String[] nullProperties = getNullPropertyNames(src, false);
        copyProperties(src, target, ArrayUtils.addAll(nullProperties, fieldNames));
    }

    /**
     * 对象转换
     *
     * @param source         源对象
     * @param targetSupplier 转换对象的lambda
     * @return 转成的对象
     */
    public static <S, T> T convertTo(S source, Supplier<T> targetSupplier) {
        return convertTo(source, targetSupplier, null);
    }

    /**
     * 对象转换
     *
     * @param source         源对象
     * @param targetSupplier 转换对象的lambda
     * @param callBack       回调方法
     * @return 转成的对象
     */
    public static <S, T> T convertTo(S source, Supplier<T> targetSupplier, ConvertCallBack<S, T> callBack) {
        if (Objects.isNull(source) || Objects.isNull(targetSupplier)) {
            return null;
        }
        T target = targetSupplier.get();
        copyPropertiesIgnoreNull(source, target);
        if (Objects.nonNull(callBack)) {
            callBack.callBack(source, target);
        }
        return target;
    }

    /**
     * 列表转换
     *
     * @param sources        源对象列表
     * @param targetSupplier 转换对象的lambda
     * @return 转成的列表
     */
    public static <S, T> List<T> convertListTo(List<S> sources, Supplier<T> targetSupplier) {
        return convertListTo(sources, targetSupplier, null);
    }

    /**
     * 列表转换
     *
     * @param sources        源对象列表
     * @param targetSupplier 转换对象的lambda
     * @param callBack       回调方法
     * @return 转成的列表
     */
    public static <S, T> List<T> convertListTo(List<S> sources, Supplier<T> targetSupplier, ConvertCallBack<S, T> callBack) {
        if (Objects.isNull(sources) || Objects.isNull(targetSupplier)) {
            return null;
        }
        List<T> list = Lists.newArrayListWithCapacity(sources.size());
        for (S source : sources) {
            T target = targetSupplier.get();
            copyPropertiesIgnoreNull(source, target);
            if (Objects.nonNull(callBack)) {
                callBack.callBack(source, target);
            }
            list.add(target);
        }
        return list;
    }

    @FunctionalInterface
    public interface ConvertCallBack<S, T> {

        /**
         * 回调函数
         *
         * @param source 源
         * @param target 目标
         */
        void callBack(S source, T target);
    }
}
